#ifndef FIELDFUNCTIONS_HPP
#define FIELDFUNCTIONS_HPP

#include "meshTypes_fwd.hpp"
#include "signalCodes.hpp"
#include "meshTypes.hpp"
#include "solverBaseTypes_fwd.hpp"
#include "fieldBase.hpp"

namespace hms
{

auto findValuesAtTime(
	const TimeSeries& values,
	scalar time
) -> TimeSeries::const_iterator;

template<typename MeshType>
Index nFieldCols( const MeshBase<MeshType>& mesh );

template<Uniformity isUniform, typename MeshType>
void resizeFieldValues(
	ArrayXXs& values,
	Index nComponents,
	[[maybe_unused]] const MeshBase<MeshType>& mesh
);

template<typename MeshType>
void resizeFieldValues(
	ArrayXXs& values,
	Index nComponents,
	const MeshBase<MeshType>& mesh,
	Uniformity isUniform
);

template<typename MeshType>
void setCorners(
	ArrayXXs& values,
	const MeshBase<MeshType>& mesh,
	scalar cornerValue = 0
);


template<Uniformity isUniform, typename MeshType>
void assertFieldValuesSize(
	[[maybe_unused]] const ArrayXXs& values,
	[[maybe_unused]] const MeshBase<MeshType>& mesh
);


template<typename T, typename MeshType>
using DomainValuesType =
	std::conditional_t<std::is_base_of_v<StructMeshBase<MeshType>, MeshType>,
	/* removed all the default template parameters, as for the true type of the
	 * std::conditional, everything actually stays at default.
	 * For the false type, the last parameter is InnerPanel, which defaults to
	 * false, but is true here, so the parameters must stay. */
		Eigen::Reshaped<Eigen::Block<Eigen::Reshaped<std::remove_reference_t<T>>>>,
		Eigen::Block<std::remove_reference_t<T>, -1, -1, true>
	>;

/* works for const and non-const types,
 * but at the cost of that horrible return type */
template<typename T, typename MeshType>
DomainValuesType<T, MeshType> domainValues(
	T&& values, const MeshBase<MeshType>& mesh
);

template<typename Derived, typename MeshType>
bool hasMatchingSize(
	const Eigen::DenseBase<Derived>& values,
	const MeshBase<MeshType>& mesh,
	Index rows = signalCode::uninitialised
);

/* Sets field values through the passed in function parsers.
 * Time and time step are considered.
 * When the template parameter isUniform is set to Uniform,
 * the zField argument is ignored. If it is set to NonUniform, it is used.
 * Use this one for source fields with updateMethod == function */
template<Uniformity isUniform, typename MeshType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const MeshBase<MeshType>&,
	const Time&,
	std::experimental::observer_ptr<const Field<MeshType>> zField
);


/* Sets field values through the passed in function parsers.
 * Use this overload for one-time field value initialisation,
 * as no time values are used.
 * When Uniformity is set to Uniform OR the bool parameter is set to false,
 * then z values are ignored. This is useful for setting the z field itself
 * by functions */
template<Uniformity isUniform, typename MeshType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const MeshBase<MeshType>& mesh,
	const Field<MeshType>& z,
	bool useZField
);

/* Sets field values through the passed in function parsers.
 * Convencience overload to use with Solver objects. Chains to
 * void setFieldByFunctions(
 *    ArrayXXs&,
 *    const std::vector<FunctionParserWrapper>&,
 *    const MeshBase<MeshType>&,
 *    const Field<MeshType>& z,
 *    bool useZField
 * );
 * Therefore, doesn't use time values and conditionally uses z values */
template<Uniformity isUniform, typename SolverType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const SolverBase<SolverType>& solver,
	bool useZField
);


/* Sets field values through the passed in function parsers.
 * Convencience overload to have Uniformity as non-template parameter.
 * Chains to
 * void setFieldByFunctions(
 *    ArrayXXs& vals,
 *    const std::vector<FunctionParserWrapper>& functions,
 *    const SolverBase<SolverType>& solver,
 *    bool useZField
 * );
 * Therefore, doesn't use time values and conditionally uses z values */
template<typename SolverType>
void setFieldByFunctions(
	ArrayXXs& vals,
	const std::vector<FunctionParserWrapper>& functions,
	const SolverBase<SolverType>& solver,
	Uniformity isUniform,
	bool useZField
);

} // end namespace hms

#include "fieldFunctions.tpp"

#endif